#include "stdafx.h"
#include "GlobalData.h"
#include "KnowledgeBase.h"

UINT GlobalData::Setting = 0;
GlobalData* GlobalData::pdata = NULL;
GlobalData* GlobalData::GetInstance()
{
	if(!pdata)
		pdata = new GlobalData();
	return pdata;
}
GlobalData::GlobalData()
{
	mapFileBase = NULL;
	mapFileHandle = NULL;
	mapFileKnowledgeBase = NULL;
	mapFileBaseorderBase = NULL;
	baseFreqItemCount = 0;
	HKEY hKey;
	DWORD dwConvSet;
	DWORD dwData = sizeof(DWORD);
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,REG_PATH,0,KEY_READ,&hKey) == ERROR_SUCCESS ){
		if( RegQueryValueEx( hKey,_T("ConversionSet"),NULL,NULL,(LPBYTE)&dwConvSet,&dwData) == ERROR_SUCCESS )
		{
			Setting = dwConvSet;
		}
		RegCloseKey(hKey);
	}
}

LPBYTE GlobalData::GetBmpAddr(std::string key)
{
	return this->baseDic[key];
}
UINT GlobalData::GetBaseFreq(CONST LPTSTR lpKey) CONST
{
	UINT start = 0;
	UINT end = this->baseFreqItemCount - 1;
	UINT structSize = sizeof(BaseFreq);
	while(start <= end)
	{
		UINT middle = start + (end - start) / 2;
		BaseFreq* p_middle = (BaseFreq*)((LPBYTE)this->mapFileBaseorderBase + middle * structSize);
		UINT ret = _tcscmp(lpKey,p_middle->baseUnit);
		if(ret > 0)
			start = middle + 1;
		else if(ret == 0)
			return p_middle->frequency;
		else
			end = middle - 1;
	}
	return 0;
}

VOID GlobalData::_createMapFile()
{
	BOOL fExist = FALSE;
	if ((mapFileHandle=CreateFileMapping(INVALID_HANDLE_VALUE,
						NULL,
						PAGE_READWRITE ,
						0,
						MAXMAPFILELEN,
						MAPFILENAME)) == NULL){
		MessageBox(NULL,_T("can not create filemapping"),_T("Init"),MB_OK);
		exit(1);
	}
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
		fExist = TRUE;
	}
	if ( (mapFileBase = (LPVOID) MapViewOfFile( mapFileHandle,
							FILE_MAP_ALL_ACCESS ,
							0,
							0,
							0)) == NULL) {
		MessageBox(NULL,_T("can not create filemapping"),_T("Init"),MB_OK);
		exit(1);
	}
	_loadData(!fExist);
}

VOID GlobalData::_loadData(BOOL reloadMapFile)
{
	FILE* stream;
	LPBYTE mapFileOffset = (LPBYTE)this->mapFileBase;
	TCHAR szPhraseFileName[MAX_PATH],szTemp[200];
	LPTSTR lpPhraseFileName;
	lpPhraseFileName = szPhraseFileName;
	lpPhraseFileName += GetSystemDirectory(szPhraseFileName,MAX_PATH);
	if (*(lpPhraseFileName-1) != _T('\\'))
		*lpPhraseFileName++ = _T('\\');
	_tcscpy(lpPhraseFileName,PYGINPUT_DB_FILE);

	if( (stream = fopen(stdex::ToMbString(szPhraseFileName).c_str(), "rb"))== NULL ){
		_stprintf(szTemp,_T("%s can not found"),szPhraseFileName);
		MessageBox(NULL,szTemp,_T("DicInit"),MB_OK);
	}

	this->mapFileImageBase = this->mapFileBase;
	this->_loadBaseImg(&stream,reloadMapFile);
	this->_loadCode(&stream,reloadMapFile);
	this->_loadBaseFreq(&stream,reloadMapFile);

	fclose(stream);
}

VOID GlobalData::_loadBaseImg(FILE** lpFile,BOOL reloadMapFile)
{
	FILE* stream = *lpFile;
	LPBYTE mapFileOffset = (LPBYTE)this->mapFileBase;
	UINT length = 0;
	fread(&length,sizeof(UINT),1,stream);
	UINT counter = 0;

	while(!feof(stream) && counter < length)
	{
		UINT fileLen = 0;
		char file[MAX_PATH];
		UINT fileDataLen = 0;
		BYTE fileData[MAXFILELEN];

		fread(&fileLen,sizeof(UINT),1,stream);
		counter += sizeof(UINT);
		fread(file,fileLen,1,stream);
		counter += fileLen;
		file[fileLen] = '\0';
		fread(&fileDataLen,sizeof(UINT),1,stream); 
		counter += sizeof(UINT);
		fread(fileData,fileDataLen,1,stream);
		counter += fileDataLen;
		std::string key = std::string(file);
		this->baseDic[key] = mapFileOffset;
		if(reloadMapFile)
		{
			if(mapFileOffset - mapFileBase < MAXMAPFILELEN - fileDataLen)
			{
				memcpy(mapFileOffset,fileData,fileDataLen);
			}
			else
			{
				MessageBox(NULL,_T("Map File Too Small."),_T("Init"),MB_OK);
				break;
			}
		}
		mapFileOffset += fileDataLen;
	}	
	this->mapFileKnowledgeBase = mapFileOffset;
}

VOID GlobalData::_loadCode(FILE** lpFile,BOOL reloadMapFile)
{
	FILE* stream = *lpFile;
	LPBYTE mapFileOffset = (LPBYTE)this->mapFileKnowledgeBase;

	UINT length = 0;
	fread(&length,sizeof(UINT),1,stream);
	UINT counter = 0;
	UINT len = sizeof(KNOWLEDGE);

	while(!feof(stream) && counter < length)
	{
		KNOWLEDGE knowledge;

		fread(&knowledge,len,1,stream);
		counter += len;

		codeList.push_back(mapFileOffset);
		if(reloadMapFile)
		{
			if(mapFileOffset - mapFileBase < MAXMAPFILELEN - len)
			{
				mapFileOffset += WriteKnowledge(&knowledge,mapFileOffset);
			}
			else
			{
				MessageBox(NULL,_T("Map File Too Small."),_T("Init"),MB_OK);
				break;
			}
		}
		else
			mapFileOffset += WriteKnowledge(&knowledge,NULL);
		
	}
	this->mapFileBaseorderBase = mapFileOffset;
}

VOID GlobalData::_loadBaseFreq(FILE** lpFile,BOOL reloadMapFile)
{
	FILE* stream = *lpFile;
	LPBYTE mapFileOffset = (LPBYTE)this->mapFileBaseorderBase;
	UINT length = 0;
	fread(&length,sizeof(UINT),1,stream);
	UINT counter = 0;
	this->baseFreqItemCount = 0;
	UINT len = sizeof(BaseFreq);

	while(!feof(stream) && counter < length)
	{
		BaseFreq baseFreq;
		fread(&baseFreq,len,1,stream);
		counter += len;

		if(reloadMapFile)
		{
			if(mapFileOffset - mapFileBase < MAXMAPFILELEN - len)
			{
				memcpy(mapFileOffset,&baseFreq,len);
				mapFileOffset += len;
			}
			else
			{
				MessageBox(NULL,_T("Map File Too Small."),_T("Init"),MB_OK);
				break;
			}
		}
		this->baseFreqItemCount += 1;
	}
}

CONST std::list<LPBYTE>* GlobalData::GetCodeList() CONST
{
	return &this->codeList;
}

VOID GlobalData::Initial()
{
	_createMapFile();
}
VOID GlobalData::Dispose()
{
	UnmapViewOfFile(this->mapFileBase);
	CloseHandle(this->mapFileHandle);
	this->baseDic.clear();
}
UINT GlobalData::WriteKnowledge(CONST LPKNOWLEDGE lpKnowledge,LPBYTE lpAddr)
{	
	KNOWLEDGEIMMAP klm;
	UINT codeLen = _tcslen(lpKnowledge->wordCode);
	UINT structSize = sizeof(KNOWLEDGEIMMAP);
	UINT size = structSize + (codeLen + 1) * sizeof(TCHAR);
	klm.dwSize = size;
	klm.dwCodeOffset = structSize;
	klm.dwCodeLen = codeLen;
	klm.pinYin = lpKnowledge->pinYin;
	klm.frequency = lpKnowledge->frequency;
	_tcscpy(klm.word,lpKnowledge->word);
	if(lpAddr)
	{
		memcpy(lpAddr,&klm,structSize);
		memcpy(lpAddr + structSize,lpKnowledge->wordCode,(codeLen + 1) * sizeof(TCHAR));
	}
	return size;
}